#pragma once
#ifndef GRAPHICSMANAGER_H_
#define GRAPHICSMANAGER_H_

#include "baseobject.h"
#include "color.h"

namespace EPOC
{
	class BaseApplication;
	class GraphicsObject;
	class Texture;
	class TextureSDL;
	class TextureOGL;
	class Image;
	class Animation;
	class Text;
	class ColorRGB;

	/**
	* Graphics manager class
	* This class handle, create and destroy all graphical resources in EPOC.
	*/
	class GraphicsManager :	public BaseObject
	{
	public:
		
		/**
		* Contructor
		* Resets all members to the default values
		*/
		GraphicsManager(BaseApplication *pApplication);
		
		/**
		* Destructor
		* Calls Cleanup()
		* @see Cleanup()
		*/
		virtual ~GraphicsManager();

		/**
		* Cleans up any relevant data
		* @see ~GraphicsManager()
		*/
		virtual void Cleanup();

		/**
		* Initializes any relevant data
		* @return False if the initialization fails, true if it succeeds.
		*/
		virtual bool Initialize();

		/**
		* Updates the animation
		* @remark This function is called once per frame internally by EpEngine
		* @param fTimeDelta the time that has passed since last update, measured in milliseconds
		* @return true if the update succeeded and false if it didn't
		*/
		virtual bool Update(float fTimeDelta);

		
		/**
		* Renders all graphical content created by this manager
		* @remark This function is called once per frame internally by EpEngine
		* @param pRenderer The SDL renderer to render the graphical content
		*/
		virtual void Render(SDL_Renderer* pRenderer);

		/**
		* Adds a pointer to a GraphicsObject to the render container.
		* @param pObject Pointer to the GraphicsObject to add to the render container.
		* @remark You normally shouldn't have to use this method!! 
		* It's mostly used internally by EPOC, but is also available for more advanced cases. The graphics manager adds all graphical content that it creates to this container automaticly.
		*/
		virtual void AddRenderObject(GraphicsObject* pObject);
		
		/**
		* Adds a pointer to a Image object to the image container.
		* @param pObject Pointer to the Image to add to the image container.
		* @remark You normally shouldn't have to use this method!! It's mostly used internally by EPOC, but is also available for more advanced cases. 
		* Use the Create and Destroy equivalents instead.
		* @see CreateImage()
		*/
		virtual void AddImageObject(Image* pObject);
		
		/**
		* Removes a pointer to a Image object from the image container.
		* @param pObject Pointer to the Image to remove from the image container.
		* @remark You normally shouldn't have to use this method!! It's mostly used internally by EPOC, but is also available for more advanced cases. 
		* Use the Create and Destroy equivalents instead.
		* @see DestroyImage()
		*/
		virtual void RemoveImageObject(Image* pObject);
		
		/**
		* Adds a pointer to a Animation object to the animation container.
		* @param pObject Pointer to the Animation to add to the animation container.
		* @remark You normally shouldn't have to use this method!! It's mostly used internally by EPOC, but is also available for more advanced cases. 
		* Use the Create and Destroy equivalents instead.
		* @see CreateAnimation()
		*/
		virtual void AddAnimationObject(Animation* pObject);
		
		/**
		* Removes a pointer to a Animation object from the animation container.
		* @param pObject Pointer to the Animation to remove from the animation container.
		* @remark You normally shouldn't have to use this method!! It's mostly used internally by EPOC, but is also available for more advanced cases. 
		* Use the Create and Destroy equivalents instead.
		* @see DestroyAnimation()
		*/
		virtual void RemoveAnimationObject(Animation* pObject);
		
		/**
		* Adds a pointer to a Text object to the text container.
		* @param pObject Pointer to the Animation to add to the animation container.
		* @remark You normally shouldn't have to use this method!! It's mostly used internally by EPOC, but is also available for more advanced cases. 
		* Use the Create and Destroy equivalents instead.
		* @see CreateText()
		*/
		virtual void AddTextObject(Text* pObject);

		/**
		* Removes a pointer to a Text object from the text container.
		* @param pObject Pointer to the Text to remove from the text container.
		* @remark You normally shouldn't have to use this method!! It's mostly used internally by EPOC, but is also available for more advanced cases. 
		* Use the Create and Destroy equivalents instead.
		* @see DestroyText()
		*/
		virtual void RemoveTextObject(Text* pObject);

		/**
		* Creates an texture from a file, creates an Image object to handle the texture and adds it to the image container and the render container. 
		* Textures are by default shared in EPOC which means that EPOC will only create one instance of textures with the same filepath in memory.
		* @param sFilePath Path to the file to load
		* @param bExclusive Flag telling if the texture of this image texture be exclusivly loaded, if set to true the sharing 
		* functionality is ignored and this image get an exclusive texture.
		* @param bEditable Flag telling if the texture should be editable (i.e. allow pixel operations). Only important when EPOC is setup to render in OpenGL mode.
		* @return If creation succeeds a pointer to the created Image is returned, otherwise NULL is returned.
		* @remark The bExclusive flag is almost a must to set to true if you plan to do pixel operations on the texture.
		* If you do pixel operations on a shared texture, every image that uses that texture is affected.
		* @see CreateImage(Texture *pTexture)
		*/
		virtual Image* CreateImage(std::string sFilePath, bool bExclusive = false, bool bEditable = false);

		/**
		* Creates an Image object to handle the texture supplied and adds it to the image container and the render container. 
		* @return If creation succeeds a pointer to the created Image is returned, otherwise NULL is returned.
		* @see CreateImage()
		*/
		virtual Image* CreateImage(Texture *pTexture);

		/**
		* Destroys the supplied Image object, removes it from the graphics containers and destroys the texture contained in the 
		* image if theres no more references to it.
		* @param pImage a pointer to the Image to destroy.
		* @remark This method is intended for images created by the graphics manager, all other objects will be ignored.
		*/
		virtual void DestroyImage(Image* pImage);
		
		/**
		* Creates an texture from a file, creates an Animation object to handle the texture and adds it to the animation container and the render container. 
		* Textures are by default shared in EPOC which means that EPOC will only create one instance of textures with the same filepath in memory.
		* @param sFilePath Path to the file to load
		* @param bExclusive Flag telling if teh texture of this image texture be exclusivly loaded, if set to true the sharing 
		* functionality is ignored and this animation get an exclusive texture.
		* @param bEditable Flag telling if the texture should be editable (i.e. allow pixel operations). Only important when EPOC is setup to render in OpenGL mode.
		* @return If creation succeeds a pointer to the created Animation is returned, otherwise NULL is returned.
		* @remark The bExclusive flag is almost a must to set to true if you plan to do pixel operations on the texture.
		* If you do pixel operations on a shared texture, every image that uses that texture is affected.
		* @see CreateAnimation(Texture *pTexture)
		*/
		virtual Animation* CreateAnimation(std::string sFilePath, bool bExclusive = false,  bool bEditable = false);

		/**
		* Creates an Animation object to handle the texture supplied and adds it to the animation container and the render container. 
		* @return If creation succeeds a pointer to the created Animation is returned, otherwise NULL is returned.
		* @see CreateAnimation()
		*/
		virtual Animation* CreateAnimation(Texture *pTexture);

		/**
		* Destroys the supplied Animation object, removes it from the graphics containers and destroys the texture contained in the 
		* animation if theres no more references to it
		* @param pAnimation a pointer to the Animation to destroy.
		* @remark This method is intended for animatons created by the graphics manager, all other objects will be ignored.
		*/
		virtual void DestroyAnimation( Animation* pAnimation);


		/**
		* Creates an Text object and adds it to the text container and the render container.
		* @param sText The text to display (this can be changed anytime)
		* @param pTextColor The color of the text
		* @param nFontSize The font size of the text
		* @param sFontPath The path to the font to use when rendering the text
		* @remark Supports TrueType fonts
		* @return If creation succeeds a pointer to the created Text is returned, otherwise NULL is returned.
		*/
		virtual Text* CreateText(std::string sText, ColorRGB rTextColor = ColorRGB(255, 255, 255), int nFontSize = DEFAULT_FONT_SIZE, std::string sFontPath = DEFAULT_FONT);

		/**
		* Destroys the supplied Text object and removes it from the graphics containers.
		* @param pText a pointer to the Text to destroy.
		* @remark This method is intended for animatons created by the graphics manager, all other objects will be ignored.
		*/
		virtual void DestroyText(Text* pText);
		
		/**
		* Creates an empty texture and adds it to the texture container
		* Textures are by default shared in EPOC which means that EPOC will only create one instance of textures with the same filepath in memory.
		* @param sName Name to identify the texture.
		* @param bExclusive Flag telling if the texture should be exclusive, if set to true the sharing functionality is ignored and 
		* this texture is created as an exclusive texture.
		* @param bEditable Flag telling if the texture should be editable (i.e. allow pixel operations). Only important when EPOC is setup to render in OpenGL mode.
		* @return If creation succeeds a pointer to the created Texture is returned, otherwise NULL is returned.
		* @remark The bExclusive flag is almost a must to set to true if you plan to do pixel operations on the texture.
		* If you do pixel operations on a shared texture, every object refering to the texture is affected.
		* @see LoadTextureFromFile()
		* @see GetTexture()
		*/
		virtual Texture* CreateTexture(std::string sName, bool bExclusive = false, bool bEditable = false);
		
		/**
		* Destroys the supplied Texture object, removes it from the graphics containers and destroys the texture if theres no more references to it
		* @param pTexture a pointer to the Texture to destroy.
		* @remark This method is intended for textures created by the graphics manager, all other objects will be ignored.
		*/
		virtual bool DestroyTexture(Texture *pTexture);

		/**
		* Loads a texture from file
		* @param sFilePath Filepath to the texture.
		* @param bExclusive Flag telling if the texture should be exclusive, if set to true the sharing functionality is ignored and 
		* this texture is created as an exclusive texture.
		* @param bEditable Flag telling if the texture should be editable (i.e. allow pixel operations). Only important when EPOC is setup to render in OpenGL mode.
		* @return If creation succeeds a pointer to the created Texture is returned, otherwise NULL is returned.
		* @remark The bExclusive flag is almost a must to set to true if you plan to do pixel operations on the texture.
		* If you do pixel operations on a shared texture, every object refering to the texture is affected.
		* @remark This method is used internally by CreateTexture(..) and should only be used if you know what you are doing.
		* @remark Supports the following formats: BMP, GIF, JPEG, LBM, PCX, PNG, PNM, TGA, TIFF, XCF, XPM, XV
		* @see CreateTexture()
		* @see GetTexture()
		*/
		virtual Texture* LoadTextureFromFile(std::string sFilePath, bool bExclusive = false, bool bEditable = false);
		
		/**
		* Returns a pointer to an Texture, from the graphics resources, using the name of the texture
		* @param sName Name to identify the texture
		* @return If the texture exists in the resources a pointer to it is returned, otherwise NULL is returned.
		* @remark This method is used internally by CreateTexture(..) and should only be used if you know what you are doing.
		* @see CreateTexture()
		* @see LoadTextureFromFile()
		*/
		virtual Texture* GetTexture(std::string sName);
		
		/**
		* Adds a texture to the graphics resources
		* @param pTexture
		* @return If the texture exists in the resources a pointer to it is returned, otherwise NULL is returned.
		* @remark This method is used internally by CreateTexture(..) and should only be used if you know what you are doing.
		* @see CreateTexture()
		* @see LoadTextureFromFile()
		* @see GetTexture()
		*/
		virtual void AddTexture(Texture *pTexture);

		/**
		* Removes a texture from the graphics resources
		* @param pTexture
		* @return True if the texture exists in the resources and was successfully removed, otherwise false is returned.
		* @remark This method is used internally by DestroyTexture(..) and should only be used if you know what you are doing.
		* @see DestroyTexture()
		* @see LoadTextureFromFile()
		* @see AddTexture()
		*/
		virtual bool RemoveTexture(Texture *pTexture);
		
		/**
		* Sets a flag telling if the graphics manager should sort the rendercontainer before rendering or not.
		* @param bStatus If set to true the render container will be sorted before rendering, if set to false it will be rendered as it is.
		*/
		virtual void SetRenderSorting(bool bStatus);

		/**
		* Sets a flag telling if the graphics manager sould sort the rendercontainer before rendering or not.
		* @param bStatus If set to true the render container will be sorted before rendering, if set to false it will be rendered as it is.
		*/
		virtual void SetClearColor(ColorRGB rColor);
			
	// public helpers
		inline bool IsDebugInfoEnabled()					{ return m_bShowDebugInfo;}
		inline void SetDebugInfo(bool bStatus)				{ m_bShowDebugInfo = bStatus;}
		inline bool IsRenderSortingEnabled()				{ return m_bSortRenderContainer;}
		
	protected:
		virtual bool InitializeOpenGL();
		virtual void RenderSDL(TextureSDL* pDestSurface);
		virtual void RenderOGL(SDL_Renderer* pRenderer);
		virtual bool UpdateDebugInformation(float fTimeDelta);
		
		virtual void SortRenderContainer(std::vector<class GraphicsObject*> *pRenderContainer);
		virtual void SortAlphaRenderContainer(std::vector<class GraphicsObject*> *pRenderContainer);
		
		static int ZOrderGreater(GraphicsObject *pObject1, class GraphicsObject *pObject2);
		static int ZOrderSmaller(GraphicsObject *pObject1, class GraphicsObject *pObject2);

	protected:
		int m_nFrameCounter;
		int m_nRenderCandidates;
		int m_nRenderedObjects;

		bool m_bShowDebugInfo;
		bool m_bSortRenderContainer;
		
		Text *m_pDebugText;
		ColorRGB m_rClearColor;

		std::list<GraphicsObject*> m_rImages;
		std::list<GraphicsObject*> m_rAnimations;
		std::list<GraphicsObject*> m_rTexts;

		std::list<std::list<GraphicsObject*> *> m_rUpdateContainer;
		std::vector<GraphicsObject*> m_rRenderContainer;
		std::vector<GraphicsObject*> m_rAlphaRenderContainer;
		std::list<Texture*> m_rTextures;

		BaseApplication *m_pApplication;
	
	}; // class GraphicsManager
}; //namespace EPOC

#endif